﻿using Ajr.DigitaPlatform.Common.Base;
using Ajr.DigitaPlatform.DeviceAccess.Base;
using Ajr.DigitaPlatform.DeviceAccess.Transfer;
using Ajri.DigitaPlatform.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Ajr.DigitaPlatform.DeviceAccess.Execute
{
    public class ModbusRTU : ModbusBase
    {

        /// <summary>
        /// 查找共用的通信对象
        /// </summary>
        /// <param name="props">参数集合 属性键值对</param>
        /// <param name="tos"></param>
        //internal override Result Match(List<DevicePropItemEntity> props, List<TransferObject> tos)
        //{
        //    Result result = new Result();
        //    try {
        //        this.Props = props;

        //        var prop = props.FirstOrDefault(p => p.PropName == "Endian");
        //        if (prop != null)
        //        {
        //            this.EndianType = (EndianType)Enum.Parse(typeof(EndianType), prop.PropValue);
        //        }

        //        // PortName
        //        //tos.
        //        //this.TransferObject = new SerialUnit();

        //        // 这一步取出组件中维护的所有PortName属性值 
        //        var ps = props.Where(p => p.PropName == "PortName").Select(p => p.PropValue).ToList();

        //        // 从tos列表中找到PortName值一样的对象
        //        this.TransferObject = tos.FirstOrDefault(
        //            to =>
        //            to.GetType().Name == "SerialUnit" &&
        //            ps.All(s => to.Conditions.Any(c => c == s))  // 匹配两上集合是否一致
        //            );
        //        //this.TransferObject = tos.FirstOrDefault(FindTransfer);

        //        if (this.TransferObject == null)
        //        {
        //            this.TransferObject = new SerialUnit();

        //            this.TransferObject.Conditions = ps;
        //            tos.Add(this.TransferObject);

        //            // 初始化相关属性()
        //            Result result_config = this.TransferObject.Config(props);
        //            if (!result_config.Status)
        //                return result_config;
        //    }
        //    }
        //    catch (Exception ex) {
        //        result.Status = false;
        //        result.Message = ex.Message;
        //    }
        //    return result;
        //}
        /// <summary>
        /// 查找共用的通信对象
        /// </summary>
        /// <param name="props">参数集合   属性键值对</param>
        /// <param name="tos"></param>
        /// 优化后
        internal override Result Match(List<DevicePropItemEntity> props, List<TransferObject> tos)
        {
            var ps = props.Where(p => p.PropName == "PortName").Select(p => p.PropValue).ToList();
            return this.Match(props, tos, ps, "SerialUnit");
        }
        /// 优化后
        //public override Result Read(List<CommAddress> mas)
        //{
        //    /// 1、变量离散   不连续
        //    /// 2、可以每个每个的变量进行数据读取，效率低，  需要做打包
        //    ///     40001、40010、40080、40100、40200、00001、00002
        //    /// 3、通信库封装的时候，做了一个动作，长度自动分片
        //    /// 4、进行同类存储区的地址整合
        //    ///    40001 -》  请求200个
        //    ///    03   01  200
        //    /// 
        //    /// 
        //    /// 5、200个地址    把5个地址对应的数据挑出来，写入到参数中去

        //    Result result = new Result();
        //    try
        //    {

        //        var prop = this.Props.FirstOrDefault(p => p.PropName == "SlaveId");
        //        if (prop == null)
        //            throw new Exception("未配置从站地址");

        //        byte slaveId = 0x01;
        //        byte.TryParse(prop.PropValue, out slaveId);

        //       // List<ModbusAddress> mas = this.GroupAddress(variables);
        //        foreach (var ma in mas)
        //        {
        //            ///打包地址没有做长度检查
        //            // ushort max = 120;// 默认情况  每次请求最多支持120个寄存器
        //            //int reqTotalCount = ma.Length;// 一共需要读多少个寄存器，ma.Length-》寄存器数据   一个寄存器两个字节
        //            //List<byte> bytes = new List<byte>();
        //            //ushort startAddr = (ushort)ma.StartAddress;
        //            //if (ma.FuncCode == 0x01 || ma.FuncCode == 0x02)
        //            //{
        //            //    // 如果是线圈，每次请求最多支持120*8*2个状态     8个状态使用一个字节
        //            //    reqTotalCount = ma.Length / 8 + 1;
        //            //    max = 240 * 8;
        //            //}
        //            ushort max = 120;//寄存器个数

        //            int reqTotalCount = ma.Length;
        //            if(ma.FuncCode==0x01  || ma.FuncCode == 0x02)
        //            {
        //                max = 240 * 8;
        //            }
        //            List<byte> bytes = new List<byte>();
        //            ushort startAddr = (ushort)ma.StartAddress;
        //            for (ushort i = 0; i < reqTotalCount; i += max)
        //            {
        //                //先读120 然后在读120+ 再把所有报文存到byte里面
        //                startAddr += i;
        //                //获取最小值,将最小值的字节数传入byte 如果reqTotalCount》120 则将max传入byte
        //                var perCount = (ushort)Math.Min(reqTotalCount - i, max);

        //                var dataBytesLen = perCount * 2;
        //                if (ma.FuncCode == 0x01 || ma.FuncCode == 0x02)
        //                    dataBytesLen = (int)Math.Ceiling(perCount * 1.0 / 8);

        //                bytes.AddRange(this.Read(slaveId, (byte)ma.FuncCode, startAddr, perCount, (ushort)(perCount * 2)));
        //            }
        //            // 寄存器
        //            if ( new int[] {03,04}.Contains(ma.FuncCode))
        //            {

        //               for(int i = 0;i < ma.Addresses.Count; i++)
        //                { //需要知道变量列表 长度
        //                  // item.Addresses[0].StartAddress
        //                  //item.Addresses[0].Length
        //                  var addr = ma.Addresses[i];
        //                    var start = addr.StartAddress - ma.StartAddress;
        //                    var len = addr.Length * 2;
        //                    byte[] dataBytes = bytes.GetRange(start * 2, len).ToArray();
        //                    addr.ValueBytes = this.SwitchEndianType(new List<byte>( dataBytes)).ToArray();
        //                }



        //            }
        //            // 线圈  Byte->8个状态
        //            else if(new int[] {01,02}.Contains(ma.FuncCode))
        //            {
        //                //状态全部转换出来
        //                //更具位置进行结果获取 0x00 0x01
        //                List<byte> resultState = new List<byte>();
        //                bytes.ForEach(b =>
        //                {
        //                    for (int i = 0; i < 8; i++)
        //                    {
        //                        resultState.Add((byte)((b & (1 << i)) >> i));
        //                    }
        //                });
        //                for(int i = 0;i< ma.Addresses.Count; i++)
        //                {
        //                    var addr = ma.Addresses[i];
        //                    var start = addr.StartAddress - ma.StartAddress;
        //                    addr.ValueBytes = resultState.GetRange(start,1).ToArray();
        //                }
        //            }

        //        }
        //    }
        //    catch (Exception ex)
        //    {
        //        result.Status = false;
        //        result.Message = ex.Message;
        //    }
        //    return result;
        //}

        protected override List<byte> Read(byte slaveNum, byte funcCode, ushort startAddr, ushort count, ushort respLen)
        {
            // 一、组建请求报文
            List<byte> dataBytes = this.CreateReadPDU(slaveNum, funcCode, startAddr, count);
            // 二、计算关拼接CRC校验码
            CRC16(dataBytes);
            // 三、打开/检查通信组件的状态
            var prop = this.Props.FirstOrDefault(p => p.PropName == "TryCount");
            int tryCount = 30;
            if (prop != null)
                int.TryParse(prop.PropValue, out tryCount);
            Result connectState = this.TransferObject.Connect(tryCount);
            if (!connectState.Status)
                throw new Exception(connectState.Message);

            // 四、发送请求报文
            prop = this.Props.FirstOrDefault(p => p.PropName == "Timeout");
            int timeout = 5000;
            if (prop != null)
                int.TryParse(prop.PropValue, out timeout);
            Result<List<byte>> resp = this.TransferObject.SendAndReceived(
                    dataBytes, // 发送的请求报文 
                    respLen + 5,
                    5, // 异常响应报文长度
                    timeout);
            if (!resp.Status)
                    throw new Exception(resp.Message);


                // 五、校验检查
                List<byte> crcValidation = resp.Data.GetRange(0, resp.Data.Count - 2);
                this.CRC16(crcValidation);
                if (!crcValidation.SequenceEqual(resp.Data))
                {
                    throw new Exception("CRC校验检查不匹配");
                    // CRC 校验失败
                }

                // 六、检查异常报文
                if (resp.Data[1] > 0x80)
                {
                    // 
                    byte errorCode = resp.Data[2];
                    throw new Exception(Errors[errorCode]);
                }
                // 七、解析
                List<byte> datas = resp.Data.GetRange(3, resp.Data.Count - 5);
                return datas;
            
            
        }
        /// <summary>
        /// 计算CRC校验码
        /// </summary>
        /// <param name="value"></param>
        /// <param name="poly"></param>
        /// <param name="crcInit"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        void CRC16(List<byte> value, ushort poly = 0xA001, ushort crcInit = 0xFFFF)
        {
            if (value == null || !value.Any())
                throw new ArgumentException("");

            //运算
            ushort crc = crcInit;
            for (int i = 0; i < value.Count; i++)
            {
                crc = (ushort)(crc ^ (value[i]));
                for (int j = 0; j < 8; j++)
                {
                    crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ poly) : (ushort)(crc >> 1);
                }
            }
            byte hi = (byte)((crc & 0xFF00) >> 8);  //高位置
            byte lo = (byte)(crc & 0x00FF);         //低位置

            value.Add(lo);
            value.Add(hi);
        }
        
    }
}
